package gates;

import register.Register;
import gates.FunctionGate;
import mathtools.Complex;
/**
 * Functional representation of a phase shift gate.
 * Generally in Matrix form on 1 qubit is diag(1,exp(i*phi))
 * This implements Gate interface, so consult this for public methods.
 * The constructor for this gate is protected, so generating such an object is left
 * for the factory class <code>Factory</code>
 * @author Jan Zaucha
 *
 */
public class PhaseShiftFunction extends FunctionGate {

	/** Store the target qubit the gate acts on*/	
	private int target;
	/** angle to shift the phase by */
	private Complex shift;


	/**
	 * Protected constructor for a functional representation of a phase shift gate
	 * Requires a target bit for the gate to operate on, however if the target bit is
	 * -1 the gate will phase shift all qubits
	 * @param target the target bit for phase shift (counting qubits from 0)
	 * @param phi the angle of phase shift exp(i*phi) in radians
	 */
	protected PhaseShiftFunction(int target, double phi){
		this.target = target+1; // since first qubit is given by 0, add one so first qubit is 1, 2nd is 2 etc...
		                        //we then phase shift all if target is 0.. 
		shift = new Complex(Math.cos(phi), Math.sin(phi));
	}

	/**
	 * Returns the name of the gate regardless of representation.
	 */
	public String getName() {
		return "Phase Shift";
	}

	/**
	 * Applies this gate to the given register. See interface <code>Gate</code> for usage.
	 * @param r the register 
	 */
	public void apply(Register r) {
		if (target > r.getNumQbits() || target < 0){
			throw new IllegalArgumentException("Target qubit not in register. Register size (qubits): " + r.getNumQbits() + " Target qubit: " + target);
		}

		// if target is flagged "-1" we phase shift all qubits
		if(target==0){
			for(int i = 1; i<r.getNumQbits()+1; i++){
				this.applyToSingleQubit(i, r);
			}
			
	
		}
		// else apply to a given single qubit
		else{
			this.applyToSingleQubit(target, r);
		}
	}
	
	/**
	 * Private method that applies phase shift gate to the given register given the target qubit. 
	 * @param targetVar target qubit for the phase shift !(count qubits from 1 to N)!
	 * @param r the register 
	 */
	private void applyToSingleQubit(int targetVar, Register r){
		int numStates = r.getHeight();    // number of states in register
		// create array to store updated register
		Complex [] newRegister = new Complex [numStates]; 
		// interval size- # of amplitudes in one interval of "function"
		int intervalSize = (int)(numStates/Math.pow(2,targetVar));
		// first j loop for number of intervals of "same sign"
		for(int j=0; j< Math.pow(2, targetVar -1); j++){
			// nested loop: first do nothing, then multiply by shift (exp(i*phi))
			for(int i=1; i <intervalSize +1; i++){
				newRegister[2*j*intervalSize +i-1] = r.getElement(0, 2*j*intervalSize +i-1);
				newRegister[intervalSize +2*j*intervalSize +i-1] = Complex.multiplyComplex(r.getElement(0, intervalSize +2*j*intervalSize +i-1), shift);
			}

		}
		// Now update our real qubits 
		r.update(newRegister);
		// normalize register
		r.normalise();

	}



}
